package com.twotwoshop.service.user.service.Impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.twotwoshop.commom.enums.UserChainMarkEnum;
import com.twotwoshop.commom.redis.RedisConstants;
import com.twotwoshop.domin.goods.pojo.Product;
import com.twotwoshop.domin.goods.vos.ProductVo;
import com.twotwoshop.domin.user.dtos.LoginDto;
import com.twotwoshop.domin.user.dtos.UserDto;
import com.twotwoshop.domin.user.vo.SellerVo;
import com.twotwoshop.fegin.api.goods.IGoodsClient;
import com.twotwoshop.service.user.service.IUserService;
import com.twotwoshop.util.RegexUtils;
import com.twotwoshop.domin.common.dtos.ResponseResult;
import com.twotwoshop.domin.common.enums.AppHttpCodeEnum;
import com.twotwoshop.util.thread.AppThreadLocalUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.twotwoshop.service.user.mapper.UserMapper;
import org.apache.commons.lang3.StringUtils;
import org.opengoofy.index12306.framework.starter.designpattern.chain.AbstractChainContext;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.twotwoshop.domin.user.pojos.ApUser;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


import static com.twotwoshop.commom.redis.RedisConstants.*;
import static com.twotwoshop.util.SystemConstants.USER_NICK_NAME_PREFIX;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, ApUser> implements IUserService {


    private final StringRedisTemplate stringRedisTemplate;

    private final UserMapper userMapper;

    private final IGoodsClient goodsClient;

    private final RedissonClient redissonClient;

    //责任链模式
    private final AbstractChainContext<LoginDto> abstractChainContext;

    //布隆过滤器
    private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;



    /**
     * 发送验证码
     *
     * @param loginDto
     * @return
     */
    @Override
    public ResponseResult sendCode(LoginDto loginDto) {

        String phone = loginDto.getPhone();


        //1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2.如果不符合 返回错误信息
            return ResponseResult.errorResult(AppHttpCodeEnum.AP_USER_DATA_NOT_EXIST, "手机号格式错误！");
        }


        //3.符合 生成验证码                  六位的验证码
        String code = RandomUtil.randomNumbers(6);


        //4.保存验证码到Redis  设置一分钟过期
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + phone, code);
        stringRedisTemplate.expire(LOGIN_CODE_KEY + phone, LOGIN_CODE_TTL, TimeUnit.MINUTES);


        //5.发送验证码
        log.error("发送验证码成功：{}", code);

        //返回成功
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }


    /**
     * 用户登录
     *
     * @param loginForm
     * @return
     */
    @Override
    public ResponseResult login(LoginDto loginForm) {

        String phone = loginForm.getPhone();

        //1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            //2.如果不符合 返回错误信息
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "手机号格式错误！");
        }


        // 从redis中获取验证码
        String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);

        //2.校验验证码  从Redis中获取验证码
        if (loginForm.getVerifyCode() == null || !loginForm.getVerifyCode().equals(code)) {
            //3.不一致 返回错误信息
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "验证码错误！");
        }

        //4.一致     根据手机号查询该用户是否存在
        ApUser user = userMapper.selectOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, loginForm.getPhone()));


        //5.不存在 创建新用户 保存该用户到数据库
        if (user == null) {
            //创建新用户
            user = createUserWithPhone(loginForm.getPhone());
        }

        //6.保存用户到Redis

        //6.1 随机生成token 作为登录令牌
        String token = UUID.randomUUID().toString();

        UserDto userDTO = BeanUtil.copyProperties(user, UserDto.class);

        //6.2 将User对象转化为Hash存储         自定义map对象类型
        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                CopyOptions.create()
                        //忽略空的值
                        .setIgnoreNullValue(true)
                        //将hash的value类型转换为String
                        .setFieldValueEditor((fieldName, fieldValue) -> fieldValue == null ? null : fieldValue.toString()));


        //6.3 存储
        stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token, userMap);

        //6.4 设置用户登录token的有效期  30分钟2
        stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.MINUTES);

        //7.返回token
        return ResponseResult.okResult(token);
    }


    //创建新用户
    private ApUser createUserWithPhone(String phone) {

        //1.创建新用户
        ApUser user = new ApUser();
        user.setPhone(phone);
        user.setName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
        user.setImage("https://web-oss-learn.oss-cn-beijing.aliyuncs.com/fdb770e7-a025-432d-9d9d-52e0d637c21c.png");

        //2.保存用户
        userMapper.insert(user);

        return user;
    }



    /*    *//**
     * 用户注册 使用布隆过滤器和分布式锁保证线程安全
     *
     * @param loginForm
     * @return
     *//*
    @Override
    public ResponseResult login(LoginDto loginForm) {

        String phone = loginForm.getPhone();

        // 执行用户注册的责任链过滤器,进行注册参数校验等处理
        abstractChainContext.handler(UserChainMarkEnum.USER_REGISTER_FILTER.name(), loginForm);

        // 获取用户注册的分布式锁,防止用户名重复注册
        RLock lock = redissonClient.getLock(LOCK_USER_REGISTER + loginForm.getPhone());

        boolean tryLock = lock.tryLock();

        if (!tryLock) {
            throw new RuntimeException("系统繁忙");
        }

        try {
            //1.校验手机号
            if (RegexUtils.isPhoneInvalid(phone)) {
                //2.如果不符合 返回错误信息
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "手机号格式错误！");
            }

            // 从redis中获取验证码
            String code = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + phone);
            if (StringUtils.isBlank(code)) {
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"验证码已过期");
            }

            //2.校验验证码  从Redis中获取验证码
            if (loginForm.getVerifyCode() == null || !loginForm.getVerifyCode().equals(code)) {
                //3.不一致 返回错误信息
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "验证码错误！");
            }

            ApUser user = null;
            try {

            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            try {
                // 一致   创建新用户
                user = new ApUser();
                user.setPhone(phone);
                user.setName(USER_NICK_NAME_PREFIX + RandomUtil.randomString(10));
                user.setImage("https://web-oss-learn.oss-cn-beijing.aliyuncs.com/fdb770e7-a025-432d-9d9d-52e0d637c21c.png");

                int insert = userMapper.insert(user);
                if (insert != 1) {
                    throw new RuntimeException("创建用户失败");
                }

                //这个异常通常用来捕获数据库插入异常
            } catch (DuplicateKeyException dke) {
                // 捕获用户名重复异常
                log.error("用户名 [{}] 重复注册", loginForm.getPhone());
                throw new RuntimeException("用户重复注册");
            }


            //6.保存用户到Redis

            //6.1 随机生成token 作为登录令牌
            String token = UUID.randomUUID().toString();

            UserDto userDTO = BeanUtil.copyProperties(user, UserDto.class);

            //6.2 将User对象转化为Hash存储         自定义map对象类型
            Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
                    CopyOptions.create()
                            //忽略空的值
                            .setIgnoreNullValue(true)
                            //将hash的value类型转换为String
                            .setFieldValueEditor((fieldName, fieldValue) -> fieldValue == null ? null : fieldValue.toString()));


            //6.3 存储
            stringRedisTemplate.opsForHash().putAll(LOGIN_USER_KEY + token, userMap);

            //6.4 设置用户登录token的有效期  30分钟2
            stringRedisTemplate.expire(LOGIN_USER_KEY + token, LOGIN_USER_TTL, TimeUnit.MINUTES);

            // 将用户手机号添加到布隆过滤器,用于后续用户手机号的快速判断
            userRegisterCachePenetrationBloomFilter.add(phone);

            //7.返回token
            return ResponseResult.okResult(token);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {

            // 释放分布式锁
            lock.unlock();
        }
    }*/

    /**
     * 获取当前用户下信息
     *
     * @return
     */
    @Override
    public ResponseResult getuserInfo() {

        ApUser user = AppThreadLocalUtil.getUser();

        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        ApUser apUser = userMapper.selectById(user.getId());
        log.error(apUser.toString());

        return ResponseResult.okResult(apUser);
    }


    /**
     * 用户登出
     *
     * @return
     */
    @Override
    public ResponseResult loginOut(String token) {

        if (StringUtils.isBlank(token)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "参数错误");
        }

        if (StringUtils.isNotBlank(token) && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        // 从redis中删除用户信息
        String key = LOGIN_USER_KEY + token;
        Boolean deleted = stringRedisTemplate.delete(key);

        if (Boolean.TRUE.equals(deleted)) {
            return ResponseResult.okResult("退出成功");
        } else {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN, "退出失败");
        }

    }


    /**
     * 更改用户信息
     *
     * @param apUser
     * @return
     */
    @Override
    public ResponseResult updateUserInfo(ApUser apUser) {

        //1.获取当前用户id
        ApUser userLocal = AppThreadLocalUtil.getUser();
        Integer userId=userLocal.getId();

        // 查询用户是否存在
        ApUser user = userMapper.selectById(userId);
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"用户不存在");
        }

        apUser.setId(userId);
        // 执行更新
        int rows = userMapper.updateById(apUser);
        if (rows != 1) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"更新失败，请重试");
        }


        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);


        //以下是用户名和用户非个人简介的敏感词审查 后续会用责任链模式进行实现

 /*       // 检查昵称是否合规
        if (!checkNickname(updateDTO.getName())) {
            throw new BusinessException("昵称包含敏感词");
        }

         // 检查个性签名是否合规
            if (!checkCertification(updateDTO.getCertification())) {
                throw new BusinessException("个性签名包含敏感词");
            }
        */
        // 只更新非空字段


    }


    /**
     * 根据用户id获取用户信息
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult getUserInfoById(Long id) {

        //1.检查参数
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.
        ApUser user = userMapper.selectById(id);
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }

        return ResponseResult.okResult(user);
    }

    /**
     * 获取卖家信息
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult getsellerInfoById(Long id) {

        //参数判空
        if (id == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //查询用户基本信息
        ApUser user = userMapper.selectById(id);
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }

        //拷贝该用户基本信息到Vo中
        SellerVo sellerVo = BeanUtil.copyProperties(user, SellerVo.class);


        //查询用户状态信息
        Long userId=id;
        ResponseResult responseResult = goodsClient.listProductsByUserId(userId);
        if (AppHttpCodeEnum.SUCCESS.getCode()!=responseResult.getCode()){
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"商品发布信息远程调用失败");
        }

        log.error(responseResult.getData().toString());

        //商家在售商品
        List<Product> productsList = JSONUtil.toList(responseResult.getData().toString(), Product.class);


        /**
         * 在售商品数
         */
        //private Long goodsCount;
        int size = productsList.size();
        sellerVo.setGoodsCount((long) size);


        /**
         *  粉丝数量
         */
        //private Long fanCount;
        Long fanCount = stringRedisTemplate.opsForZSet().size(USER_FAN_KEY + userId.toString());
        if (fanCount ==null || fanCount.intValue() <=0) {
            sellerVo.setFanCount(0L);
        }else {
            sellerVo.setFanCount(fanCount);
        }




        /**
         * 关注数量
         */
        //private Long followCount;
        Long followCount = stringRedisTemplate.opsForZSet().size(USER_FOLLOW_KEY + userId.toString());
        if (followCount ==null || followCount.intValue() <=0) {
            sellerVo.setFollowCount(0L);
        }else {
            sellerVo.setFollowCount(followCount);
        }




        /**
         * 在售商品
         */
        //private List<ProductVo> products;
        List<ProductVo> voList=new ArrayList<>();

        for (Product product : productsList) {
            ProductVo vo = new ProductVo();
            BeanUtils.copyProperties(product, vo);

            //将图片路径转化为集合
            List<String> imageList = Arrays.stream(product.getImages().split(","))
                    .map(String::trim)
                    .collect(Collectors.toList());

            vo.setImages(imageList);


            voList.add(vo);

        }

        //设置用户发布信息
        sellerVo.setProducts(voList);


        return ResponseResult.okResult(sellerVo);
    }


    /**
     * 电话号码是否存在
     *
     * @param phone 用户名
     * @return 用户名是否存在返回结果
     */
    @Override
    public Boolean hasUsername(String phone) {
        boolean hasUsername = userRegisterCachePenetrationBloomFilter.contains(phone);
        if (hasUsername) {
            return !stringRedisTemplate.opsForSet().isMember(USER_REGISTER_REUSE_SHARDING , phone);
        }
        return true;
    }




}

















